package edu.uta.cfl.engine;

import java.util.ArrayList;
import java.util.Arrays;

import edu.uta.cfl.data.Parameter;

public class Combinatorics {

	public static ArrayList<int[]> getParamCombos(int m, int n) {
		return getParamCombos(m, n, -1);
	}

	public static ArrayList<int[]> getParamCombos(int m, int n, int first) {

		ArrayList<int[]> rval = new ArrayList<int[]>();

		int[] index = new int[m];
		if (first != -1 && first <= m - n) {
			for (int i = 0; i < index.length; i++) {
				if (i < first || (i > first && i < m - n + 1)) {
					index[i] = 0;
				} else {
					index[i] = 1;
				}
			}
		} else {
			for (int i = 0; i < index.length; i++) {
				if (i < m - n) {
					index[i] = 0;
				} else {
					index[i] = 1;
				}
			}
		}

		// termination flag
		boolean exhausted = false;

		while (!exhausted) {
			// create a deep copy of index
			addCombo(rval, index);

			// find the next combo
			// 1. find the last zero followed by one and
			// count the ones before such a zero
			int pos = -1;
			int ones = 0;
			int count = 0;
			for (int i = 0; i < m - 1; i++) {
				if (index[i] == 1) {
					ones++;
				} else if (index[i] == 0 && index[i + 1] == 1) {
					pos = i;
					count = ones;
				}
			}

			// stop if no zero is followed by one
			if (pos == -1) {
				exhausted = true;
			} else {
				// change the found zero to one
				index[pos] = 1;
				if (index[m - 1] == 1) {
					// e.g. 001011 -> 001101
					index[pos + 1] = 0;
				} else {
					// e.g. 001100 -> 010001
					for (int i = pos + 1; i < m; i++) {
						if (i <= m - n + count) {
							index[i] = 0;
						} else {
							index[i] = 1;
						}
					}
				}
			}
		}

		return rval;
	}

	private static void addCombo(ArrayList<int[]> combos, int[] index) {
		int[] combo = new int[index.length];
		for (int i = 0; i < index.length; i++) {
			combo[i] = index[i];
		}
		combos.add(combo);
	}

	public static ArrayList<int[]> getValueCombos(ArrayList<Parameter> params) {
		ArrayList<int[]> rval = new ArrayList<int[]>();

		int[] index = new int[params.size()];
		Arrays.fill(index, 0);

		// termination flag
		boolean exhausted = false;

		while (!exhausted) {
			// create a deep copy of index
			addCombo(rval, index);

			// add 1 to index
			int i = index.length - 1;
			for (; i > 0; i--) {
				Parameter param = params.get(i);
				// System.out.println("getValueCombos::" + param.getName());
				if (index[i] == param.getDomainSize() - 1) {
					index[i] = 0;
				} else {
					break;
				}
			}

			if (i == 0 && index[0] == params.get(0).getDomainSize() - 1) {
				exhausted = true;
			} else {
				index[i]++;
			}
		}

		return rval;
	}
}
